#include "boost.h"

nmea_serial::nmea_serial() {

}

boost::asio::serial_port nmea_serial::init(char* serial_dev, int band_rate, int data_size) {
    
    boost::asio::io_context io_context;
    // 创建串口对象
    boost::asio::serial_port serial(io_context);

    // 打开串口
    serial.open(serial_dev); // 更改为实际串口号
    serial.set_option(boost::asio::serial_port_base::baud_rate(band_rate));
    serial.set_option(boost::asio::serial_port_base::character_size(data_size));
    serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
    serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
    serial.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
    return serial;
}


nmea_serial::~nmea_serial() {
    
}

// 函数用来处理字符串中的连续逗号
std::string nmea_serial::processCommas(const std::string& input) {
    std::string output;
    bool inCommaSequence = false;

    for (char ch : input) {
        if (ch == ',') {
            // 如果已经是逗号序列的一部分，则跳过
            if (!inCommaSequence) {
                inCommaSequence = true;
                output += ch; // 添加第一个逗号
            }
            else {
                output += '0';
                output += ch;
            }
        }
        else {
            // 如果当前字符不是逗号并且之前有逗号序列，则添加0
            inCommaSequence = false;
            output += ch;
        }
    }

    // 如果字符串以逗号结尾，需要额外添加0
    //if (inCommaSequence) {
    //    output += '0';
    //}

    return output;
}


// 处理每一行数据
std::vector<std::string> nmea_serial::process_line(const std::string& line, int &comma_count) {
    std::vector<std::string> tokens;

    // 计算逗号数量
    comma_count = std::count(line.begin(), line.end(), ',');
    std::string line_res = processCommas(line);
    //std::cout << "line_res:" << line_res << std::endl;
       
    // 解析数据
    boost::split(tokens, line_res, boost::is_any_of(","), boost::token_compress_on);

    // 在连续的逗号之间插入0，这个只适合逗号之间有空格的情形
    //std::vector<std::string> processed_tokens;
    //for (size_t i = 0; i < tokens.size(); ++i) {
    //    processed_tokens.push_back(tokens[i]);
    //    if (i + 1 < tokens.size()) {
    //        if (tokens[i].empty() && tokens[i + 1].empty()) {
    //            // 连续逗号之间插入0
    //            processed_tokens.push_back("0");
    //        }
    //    }
    //}

    // 输出解析结果和逗号数量
    //std::cout << "Received: ";
    //for (const auto& token : tokens) {
    //    std::cout << token << " ";
    //}
    //std::cout << "\nNumber of commas: " << comma_count << std::endl;

    return tokens;
}


// 计算8位异或校验和
uint8_t nmea_serial::calculateXorChecksum(const std::string& data) {
    //std::cout << "Data: " << data << std::endl;
    uint8_t checksum = 0;
    for (char c : data) {
        checksum ^= static_cast<uint8_t>(c);
    }
    return checksum;
}

// 将8位校验和转换为16进制字符串
std::string nmea_serial::checksumToHexString(uint8_t checksum) {
    std::stringstream ss;
    ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(checksum);
    return ss.str();
}


void nmea_serial::analysis_data(const std::string& line, NMEA& nmea) {
    int comma_count;    // 逗号的数量
    std::vector<std::string> tokens;

    // 计算校验和
    uint8_t xorChecksum = calculateXorChecksum(line.substr(1, line.size() - 5));    // linux->5 windows->4
    // 转换为16进制形式
    //std::string hexChecksum = checksumToHexString(xorChecksum);

    static int gngsa_num = 0;
    static int gpgsv_num = 0;
    static int glgsv_num = 0;
    int circle_num;
    static std::string prev_name = "NAME";

    tokens = process_line(line, comma_count);
    

    std::cout << "line:" << line << std::endl;
    //std::cout << "8-bit XOR checksum: " << static_cast<int>(xorChecksum) << std::endl;
    //std::cout << "Checksum in hexadecimal: " << hexChecksum << std::endl;
    //std::cout << "comma_count: " << comma_count << std::endl;
    
    int size = tokens[comma_count].length();
    
    if (tokens[0][0] == '$') {
        // 将接收到的校验和转为10进制
        unsigned recv_checksum = static_cast<unsigned char>(std::stoi(tokens[comma_count].substr(size - 3, 2), nullptr, 16));
        uint8_t recv_checksum_10 = static_cast<uint8_t>(recv_checksum);

        if (tokens[0] == "$GNGGA" && recv_checksum_10 == xorChecksum) {    // 14
            if (tokens[1].size() >= 10) {
                nmea.gngga.time_hour = std::stoi(tokens[1].substr(0, 2));
                nmea.gngga.time_minute = std::stoi(tokens[1].substr(2, 2));
                nmea.gngga.time_second = std::stoi(tokens[1].substr(4, 2));
                nmea.gngga.time_millisecond = std::stoi(tokens[1].substr(7));
            }
            if (tokens[2].size() >= 10) {
                nmea.gngga.latitude_dd = std::stoi(tokens[2].substr(0, 2));
                nmea.gngga.latitude_mm = std::stoi(tokens[2].substr(2, 2));
                nmea.gngga.latitude_mmmmm = std::stoi(tokens[2].substr(5));
            }
            nmea.gngga.latitude_direction = tokens[3][0];
            if (tokens[4].size() >= 11) {
                nmea.gngga.longitude_ddd = std::stoi(tokens[4].substr(0, 3));
                nmea.gngga.longitude_mm = std::stoi(tokens[4].substr(3, 2));
                nmea.gngga.longitude_mmmmm = std::stoi(tokens[4].substr(6));
            }
            nmea.gngga.longitude_direction = tokens[5][0];
            nmea.gngga.status = std::stoi(tokens[6]);
            nmea.gngga.satellites_number = std::stoi(tokens[7]);
            nmea.gngga.h_precision_factor = std::stof(tokens[8]);
            nmea.gngga.altitude = std::stof(tokens[9]);
            nmea.gngga.temp1 = tokens[10][0];
            nmea.gngga.geoidal_height = std::stof(tokens[11]);
            nmea.gngga.temp2 = tokens[12][0];
        }
        else if (tokens[0] == "$GNGLL" && recv_checksum_10 == xorChecksum) {   // 7
            if (tokens[1].size() >= 10) {
                nmea.gngll.latitude_dd = std::stoi(tokens[1].substr(0, 2));
                nmea.gngll.latitude_mm = std::stoi(tokens[1].substr(2, 2));
                nmea.gngll.latitude_mmmmm = std::stoi(tokens[1].substr(5));
            }
            nmea.gngll.latitude_direction = tokens[2][0];
            if (tokens[3].size() >= 11) {
                nmea.gngll.longitude_ddd = std::stoi(tokens[3].substr(0, 3));
                nmea.gngll.longitude_mm = std::stoi(tokens[3].substr(3, 2));
                nmea.gngll.longitude_mmmmm = std::stoi(tokens[3].substr(6));
            }
            nmea.gngll.longitude_direction = tokens[4][0];
            if (tokens[5].size() >= 10) {
                nmea.gngll.time_hour = std::stoi(tokens[5].substr(0, 2));
                nmea.gngll.time_minute = std::stoi(tokens[5].substr(2, 2));
                nmea.gngll.time_second = std::stoi(tokens[5].substr(4, 2));
                nmea.gngll.time_millisecond = std::stoi(tokens[5].substr(7));
            }
            nmea.gngll.pos_system_status = tokens[6][0];
            nmea.gngll.mode_indict = tokens[comma_count][0];
        }
        else if (tokens[0] == "$GNGSA" && recv_checksum_10 == xorChecksum) {   // 18
            if (prev_name == tokens[0]) {
                gngsa_num++;
            }
            else {
                gngsa_num = 0;
            }
            nmea.gngsa[gngsa_num].mode = tokens[1][0];
            nmea.gngsa[gngsa_num].pos_mode = std::stoi(tokens[2]);
            for (int i = 0; i < 12; i++) {
                nmea.gngsa[gngsa_num].use_satellites_number[i] = std::stoi(tokens[3 + i]);
            }
            nmea.gngsa[gngsa_num].loc_precision_factor = std::stof(tokens[15]);
            nmea.gngsa[gngsa_num].h_precision_factor = std::stof(tokens[16]);
            nmea.gngsa[gngsa_num].v_precision_factor = std::stof(tokens[17]);
            nmea.gngsa[gngsa_num].system_flag = tokens[comma_count][0] - '0';
        }
        else if (tokens[0] == "$GPGSV" && recv_checksum_10 == xorChecksum) {   // 20 / 8
            if (prev_name == tokens[0]) {
                gpgsv_num++;
            }
            else {
                gpgsv_num = 0;
            }
            if (comma_count == 8)
                circle_num = 1;
            else if (comma_count == 12)
                circle_num = 2;
            else if (comma_count == 16)
                circle_num = 3;
            else if (comma_count == 20)
                circle_num = 4;
            else
                circle_num = 0;

            nmea.gpgsv[gpgsv_num].totalnumsen = std::stoi(tokens[1]);
            nmea.gpgsv[gpgsv_num].sen_num = std::stoi(tokens[2]);
            nmea.gpgsv[gpgsv_num].total_num_sat = std::stoi(tokens[3]);
            for (int i = 0; i < circle_num; i++) {
                nmea.gpgsv[gpgsv_num].sat_status[i].sat_id = std::stoi(tokens[4 + i * circle_num]);
                nmea.gpgsv[gpgsv_num].sat_status[i].sat_elev = std::stoi(tokens[5 + i * circle_num]);
                nmea.gpgsv[gpgsv_num].sat_status[i].sat_az = std::stoi(tokens[6 + i * circle_num]);
                nmea.gpgsv[gpgsv_num].sat_status[i].sat_cn0 = std::stoi(tokens[7 + i * circle_num]);
            }
            nmea.gpgsv[gpgsv_num].signal_id = tokens[comma_count][0] - '0';  // static_cast<uint8_t>()
        }
        else if (tokens[0] == "$GLGSV" && recv_checksum_10 == xorChecksum) {   // 20 / 8
            if (prev_name == tokens[0]) {
                glgsv_num++;
            }
            else {
                glgsv_num = 0;
            }
            if (comma_count == 8)
                circle_num = 1;
            else if (comma_count == 12)
                circle_num = 2;
            else if (comma_count == 16)
                circle_num = 3;
            else if (comma_count == 20)
                circle_num = 4;
            else
                circle_num = 0;

            nmea.glgsv[glgsv_num].totalnumsen = std::stoi(tokens[1]);
            nmea.glgsv[glgsv_num].sen_num = std::stoi(tokens[2]);
            nmea.glgsv[glgsv_num].total_num_sat = std::stoi(tokens[3]);
            for (int i = 0; i < circle_num; i++) {
                nmea.glgsv[glgsv_num].sat_status[i].sat_id = std::stoi(tokens[4 + i * circle_num]);
                nmea.glgsv[glgsv_num].sat_status[i].sat_elev = std::stoi(tokens[5 + i * circle_num]);
                nmea.glgsv[glgsv_num].sat_status[i].sat_az = std::stoi(tokens[6 + i * circle_num]);
                nmea.glgsv[glgsv_num].sat_status[i].sat_cn0 = std::stoi(tokens[7 + i * circle_num]);
            }
            nmea.glgsv[glgsv_num].signal_id = tokens[comma_count][0] - '0';
        }
        else if (tokens[0] == "$GNRMC" && recv_checksum_10 == xorChecksum) {   // 13
            if (tokens[1].size() >= 10) {
                nmea.gnrmc.time_hour = std::stoi(tokens[1].substr(0, 2));
                nmea.gnrmc.time_minute = std::stoi(tokens[1].substr(2, 2));
                nmea.gnrmc.time_second = std::stoi(tokens[1].substr(4, 2));
                nmea.gnrmc.time_millisecond = std::stoi(tokens[1].substr(7));
            }
            nmea.gnrmc.status = tokens[2][0];
            if (tokens[3].size() >= 10) {
                nmea.gnrmc.latitude_dd = std::stoi(tokens[3].substr(0, 2));
                nmea.gnrmc.latitude_mm = std::stoi(tokens[3].substr(2, 2));
                nmea.gnrmc.latitude_mmmmm = std::stoi(tokens[3].substr(5));
            }
            nmea.gnrmc.latitude_direction = tokens[4][0];
            if (tokens[5].size() >= 11) {
                nmea.gnrmc.longitude_ddd = std::stoi(tokens[5].substr(0, 3));
                nmea.gnrmc.longitude_mm = std::stoi(tokens[5].substr(3, 2));
                nmea.gnrmc.longitude_mmmmm = std::stoi(tokens[5].substr(6));
            }
            nmea.gnrmc.longitude_direction = tokens[6][0];
            nmea.gnrmc.sog = std::stof(tokens[7]);
            nmea.gnrmc.cog = std::stof(tokens[8]);
            if (tokens[9].size() >= 6) {
                nmea.gnrmc.day = std::stoi(tokens[9].substr(0, 2));
                nmea.gnrmc.month = std::stoi(tokens[9].substr(2, 2));
                nmea.gnrmc.year = std::stoi(tokens[9].substr(4));
            }
            nmea.gnrmc.model_ind = tokens[12][0];
            nmea.gnrmc.nav_status = tokens[comma_count][0];
        }
        else if (tokens[0] == "$GNVTG" && recv_checksum_10 == xorChecksum) {   // 9
            nmea.gnvtg.cogt = std::stof(tokens[1]);
            nmea.gnvtg.temp1 = tokens[2][0];
            nmea.gnvtg.cogm = std::stof(tokens[3]);
            nmea.gnvtg.temp2 = tokens[4][0];
            nmea.gnvtg.sogn = std::stof(tokens[5]);
            nmea.gnvtg.temp3 = tokens[6][0];
            nmea.gnvtg.sogk = std::stof(tokens[7]);
            nmea.gnvtg.temp4 = tokens[8][0];
            nmea.gnvtg.mode = tokens[comma_count][0];
        }
        else if (tokens[0] == "$GNZDA") {   // 6
            if (recv_checksum_10 == xorChecksum) {
                if (tokens[1].size() >= 10) {
                    nmea.gnzda.time_hour = std::stoi(tokens[1].substr(0, 2));
                    nmea.gnzda.time_minute = std::stoi(tokens[1].substr(2, 2));
                    nmea.gnzda.time_second = std::stoi(tokens[1].substr(4, 2));
                    nmea.gnzda.time_millisecond = std::stoi(tokens[1].substr(7));
                }
                nmea.gnzda.day = std::stoi(tokens[2]);
                nmea.gnzda.month = std::stoi(tokens[3]);
                nmea.gnzda.year = std::stoi(tokens[4]);
                nmea.gnzda.temp1 = std::stoi(tokens[5]);
                if (tokens[comma_count].size() > 4)
                    nmea.gnzda.temp2 = tokens[comma_count][0] - '0';
            }
        }
        else if (tokens[0] == "$GPTXT") {   // 4
            if (recv_checksum_10 == xorChecksum) {
                nmea.gptxt.total_num_sen = std::stoi(tokens[1]);
                nmea.gptxt.sen_num = std::stoi(tokens[2]);
                nmea.gptxt.text_id = std::stoi(tokens[3]);
                //std::string::size_type n = tokens[comma_count].copy(nmea.gptxt.text_msg, 0, size - 4);
                //nmea.gptxt.text_msg[n] = '\0';
                std::memcpy(nmea.gptxt.text_msg, tokens[comma_count].c_str(), size - 4);
            }
        }
        prev_name = tokens[0];
        print_data(tokens[0], nmea, gngsa_num, gpgsv_num, glgsv_num);
    }
}

void nmea_serial::print_data(std::string name, NMEA nmea, int gngsa_num, int gpgsv_num, int glgsv_num) {
    if (name == "$GNGGA") {
        printf("$GNGGA=%d %d %d %d %d %d %d %c %d %d %d %c %d %d %f %f %c %f %c\n", nmea.gngga.time_hour, nmea.gngga.time_minute, nmea.gngga.time_second, nmea.gngga.time_millisecond,
            nmea.gngga.latitude_dd, nmea.gngga.latitude_mm, nmea.gngga.latitude_mmmmm, nmea.gngga.latitude_direction,
            nmea.gngga.longitude_ddd, nmea.gngga.longitude_mm, nmea.gngga.longitude_mmmmm, nmea.gngga.longitude_direction,
            nmea.gngga.status, nmea.gngga.satellites_number, nmea.gngga.h_precision_factor, nmea.gngga.altitude,
            nmea.gngga.temp1, nmea.gngga.geoidal_height, nmea.gngga.temp2);
    }
    else if (name == "$GNGLL") {
        printf("$GNGLL=%d %d %d %c %d %d %d %c %d %d %d %d %c %c\n", nmea.gngll.latitude_dd, nmea.gngll.latitude_mm, nmea.gngll.latitude_mmmmm, nmea.gngll.latitude_direction,
            nmea.gngll.longitude_ddd, nmea.gngll.longitude_mm, nmea.gngll.longitude_mmmmm, nmea.gngll.longitude_direction,
            nmea.gngll.time_hour, nmea.gngll.time_minute, nmea.gngll.time_second, nmea.gngll.time_millisecond,
            nmea.gngll.pos_system_status, nmea.gngll.mode_indict);
    }
    else if (name == "$GNGSA") {
        printf("$GNGSA=%c %d %d %f %f %f %d\n", nmea.gngsa[gngsa_num].mode, nmea.gngsa[gngsa_num].pos_mode, nmea.gngsa[gngsa_num].use_satellites_number[gngsa_num], nmea.gngsa[gngsa_num].loc_precision_factor,
            nmea.gngsa[gngsa_num].h_precision_factor, nmea.gngsa[gngsa_num].v_precision_factor, nmea.gngsa[gngsa_num].system_flag);
        
    }
    else if (name == "$GPGSV") {
        printf("$GPGSV=%d %d %d %d %d %d %d %d\n", nmea.gpgsv[gpgsv_num].totalnumsen, nmea.gpgsv[gpgsv_num].sen_num, nmea.gpgsv[gpgsv_num].total_num_sat,
            nmea.gpgsv[gpgsv_num].sat_status[0].sat_id, nmea.gpgsv[gpgsv_num].sat_status[0].sat_elev, nmea.gpgsv[gpgsv_num].sat_status[0].sat_az, nmea.gpgsv[gpgsv_num].sat_status[0].sat_cn0,
            nmea.gpgsv[gpgsv_num].signal_id);
    }
    else if (name == "$GLGSV") {
        printf("$GLGSV=%d %d %d %d %d %d %d %d\n", nmea.glgsv[glgsv_num].totalnumsen, nmea.glgsv[glgsv_num].sen_num, nmea.glgsv[glgsv_num].total_num_sat,
            nmea.glgsv[glgsv_num].sat_status[0].sat_id, nmea.glgsv[glgsv_num].sat_status[0].sat_elev, nmea.glgsv[glgsv_num].sat_status[0].sat_az, nmea.glgsv[glgsv_num].sat_status[0].sat_cn0,
            nmea.glgsv[glgsv_num].signal_id);
    }
    else if (name == "$GNRMC") {
        printf("$GNRMC=%d %d %d %d %c %d %d %d %c %d %d %d %c %f %f %d %d %d %d %d %c %c\n", nmea.gnrmc.time_hour, nmea.gnrmc.time_minute,  nmea.gnrmc.time_second, nmea.gnrmc.time_millisecond,
            nmea.gnrmc.status, nmea.gnrmc.latitude_dd, nmea.gnrmc.latitude_mm, nmea.gnrmc.latitude_mmmmm, nmea.gnrmc.latitude_direction,
            nmea.gnrmc.longitude_ddd, nmea.gnrmc.longitude_mm, nmea.gnrmc.longitude_mmmmm, nmea.gnrmc.longitude_direction,
            nmea.gnrmc.sog, nmea.gnrmc.cog, nmea.gnrmc.day, nmea.gnrmc.month, nmea.gnrmc.year, nmea.gnrmc.magvar, nmea.gnrmc.magvar_dir,
            nmea.gnrmc.model_ind, nmea.gnrmc.nav_status);
    }
    else if (name == "$GNVTG") {
        printf("$GNVTG=%f %c %f %c %f %c %f %c %c\n", nmea.gnvtg.cogt, nmea.gnvtg.temp1, nmea.gnvtg.cogm, nmea.gnvtg.temp2,
            nmea.gnvtg.sogn, nmea.gnvtg.temp3, nmea.gnvtg.sogk, nmea.gnvtg.temp4, nmea.gnvtg.mode);
    }
    else if (name == "$GNZDA") {
        printf("$GNZDA=%d %d %d %d %d %d %d %d %d\n", nmea.gnzda.time_hour, nmea.gnzda.time_minute, nmea.gnzda.time_second, nmea.gnzda.time_millisecond,
            nmea.gnzda.day, nmea.gnzda.month, nmea.gnzda.year, nmea.gnzda.temp1, nmea.gnzda.temp2);
    }
    else if (name == "$GPTXT") {
        printf("$GPTXT=%d %d %d %s\n", nmea.gptxt.total_num_sen, nmea.gptxt.sen_num, nmea.gptxt.text_id, nmea.gptxt.text_msg);
    }
    
}